package avicit.bdp.dgs.qa.service.engine.file;

import avicit.bdp.common.dto.StorageResourceConf;
import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.uploads.FileAdapterUtils;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dds.dispatch.enums.TaskType;
import avicit.bdp.dds.dispatch.process.ResourceInfo;
import avicit.bdp.dds.dispatch.task.shell.ShellParameters;
import avicit.bdp.dgs.qa.dao.QaDataSourceDAO;
import avicit.bdp.dgs.qa.dto.*;
import avicit.bdp.dgs.qa.service.RuleService;
import avicit.bdp.dgs.qa.service.RuleTemplateService;
import avicit.bdp.dgs.qa.service.engine.IEngine;
import avicit.bdp.dgs.qa.utils.constant.QaConstant;
import avicit.bdp.dgs.qa.utils.enums.FileType;
import avicit.bdp.ftdp.api.FtdpResourceApi;
import avicit.platform6.commons.utils.ComUtil;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2023-09-17
 * @类说明：FileEngine
 * @修改记录：
 * @注意事项：
 * @主要功能：文件校验计算引擎类
 */
@Component("file")
public class FileEngine implements IEngine {
    private static final Logger logger = LoggerFactory.getLogger(FileEngine.class);

    public static final String DEFAULT_JOB_RESULT_TABLE = "bdp_dgs_qa_job_result";

    /**
     * .inf文件后缀
     */
    public static final String INF_FILE_SUFFIX = ".inf";

    @Autowired
    private RuleTemplateService templateService;

    @Autowired
    private RuleService ruleService;

    @Autowired
    private QaDataSourceDAO qaDataSourceDAO;

    @Autowired
    private FtdpResourceApi ftdpResourceApi;

    /**
     * 数据质量计算结果表连接信息
     */
    @Value("${spring.datasource.dbType:mysql}")
    private String dbType;

    @Value("${spring.datasource.url}")
    private String jdbcUrl;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClass;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    public FileEngine() {
    }

    /**
     * 构建文件校验质量任务Json
     *
     * @param job
     * @param userOptions
     * @return
     */
    @Override
    public Map<String, Object> build(JobDTO job, Map<String, Object> userOptions) {
        // 构建ProcessDefinition
        Map<String, Object> defeMap = new HashMap<>();

        /**
         * 填充顺序
         * 1、填充Shell计算任务Json配置信息
         * 3、填充TaskNode配置信息
         * 4、填充ProcessDefinition中processDefinitionJson（流程定义信息，格式为List<TaskNode>）
         */
        // step1:填充Task Json新
        Map<String, Object> taskMap = fillTaskMap(job);

        // step2:填充ShellParameters配置信息
        ShellParameters shellParameters = fillShellParameters(job, taskMap);

        // step3:填充TaskNode配置信息
        List<Map<String, Object>> taskNodes = new ArrayList<>();
        taskNodes.add(fillTaskNode(job, shellParameters));

        // step4:填充ProcessDefinition中processDefinitionJson
        defeMap.put("globalParams", new ArrayList<>());
        defeMap.put("tasks", taskNodes);

        return defeMap;
    }

    /**
     * 填充localParameters字段
     *
     * @param job 质量作业
     * @return
     */
    public Map<String, Object> fillTaskMap(JobDTO job) {
        // step1:初始化
        Map<String, Object> localParamsMap = new HashMap<>();
        Map<String, Object> settingParamsMap = new HashMap<>();
        Map<String, Object> parameterMap = new HashMap<>();
        localParamsMap.put("settingParams", settingParamsMap);
        localParamsMap.put("parameter", parameterMap);

        // step2:填充质量任务名称
        fillTaskName(localParamsMap, job);

        // step2:填充task
        fillTaskBaseInfo(parameterMap, job);

        // step5:填充outputConfig
        fillTaskOutputConfig(parameterMap);

        // step6:填充ruleList
        fillRuleList(parameterMap, job);

        // step7:试飞工程值校验，设置文件模板
        setFCTemplateList(job.getFileType(), parameterMap);

        System.out.println(JSONObject.toJSONString(localParamsMap));

        return localParamsMap;
    }

    /**
     * 填充shell脚本配置信息
     *
     * @param job
     * @param taskMap
     * @return
     */
    private ShellParameters fillShellParameters(JobDTO job, Map<String, Object> taskMap) {
        ShellParameters shellParameters = new ShellParameters();

        // 设置shell执行搅拌
        shellParameters.setRawScript("java -jar " + ConfigUtils.getInstance().getString("spark.jar.path") + " 2 %s %s");

        // 设置Task Json参数
        ResourceInfo res = new ResourceInfo();
        res.setRes(JSONObject.toJSONString(taskMap));
        shellParameters.setResourceList(Arrays.asList(res));

        return shellParameters;
    }

    /**
     * 填充TaskNode信息
     *
     * @param job
     * @param shellParameters
     * @return
     */
    private Map<String, Object> fillTaskNode(JobDTO job, ShellParameters shellParameters) {
        Map<String, Object> taskMap = new HashMap<>();

        taskMap.put("id", ComUtil.getId());
        taskMap.put("name", job.getName());
        taskMap.put("code", "ALGORITHM");
        taskMap.put("nodeId", ComUtil.getId());
        taskMap.put("color", "#248ad8");
        taskMap.put("iconName", "algorithm");
        taskMap.put("type", TaskType.SHELL.name());
        taskMap.put("preTasks", new ArrayList<>());
        taskMap.put("preTasksObj", new HashMap<>());
        taskMap.put("runFlag", "NORMAL");
        taskMap.put("description", "质量检查任务...");
        taskMap.put("dependence", new HashMap<>());
        taskMap.put("maxRetryTimes", 0);
        taskMap.put("retryInterval", 1);
        taskMap.put("timeout", "");
        taskMap.put("taskInstancePriority", "MEDIUM");
        taskMap.put("workerGroup", "default");
        taskMap.put("params", JSONUtils.toJsonString(shellParameters));

        return taskMap;
    }

    /**
     * 填充校验任务名称
     *
     * @param localParamsMap
     * @param job
     */
    private void fillTaskName(Map<String, Object> localParamsMap, JobDTO job) {
        localParamsMap.put("name", job.getName());
    }

    /**
     * 填充校验任务基本信息，包括jobId、jobName、调度方式、校验类型
     *
     * @param parameterMap
     * @param job
     */
    private void fillTaskBaseInfo(Map<String, Object> parameterMap, JobDTO job) {
        Map<String, Object> taskMap = new HashMap<>();

        taskMap.put("id", job.getId());
        taskMap.put("name", job.getName());
        taskMap.put("schedulerType", job.getSchedulerType());
        taskMap.put("measureType", job.getMeasureType());
        taskMap.put("fileType", job.getFileType());
        taskMap.put("type", job.getType());
        taskMap.put("typeName", job.getTypeName());
        taskMap.put("modelCode", job.getModelCode());
        taskMap.put("aircraftName", job.getAircraftName());
        taskMap.put("sortie", job.getSortie());
        taskMap.put("flightDate", job.getFlightDate());
        taskMap.put("testSystemName", job.getTestSystemName());
        taskMap.put("dataProp", job.getDataProp());

        parameterMap.put("task", taskMap);
    }

    /**
     * 填充校验任务输出配置信息
     *
     * @param parameterMap
     */
    private void fillTaskOutputConfig(Map<String, Object> parameterMap) {
        Map<String, Object> outputConfigMap = new HashMap<>();

        outputConfigMap.put("dbType", this.dbType == null ? "mysql" : this.dbType);
        outputConfigMap.put("jdbcUrl", this.jdbcUrl);
        outputConfigMap.put("driverClass", this.driverClass);
        outputConfigMap.put("username", this.username);
        outputConfigMap.put("password", this.password);
        outputConfigMap.put("tableName", DEFAULT_JOB_RESULT_TABLE);

        parameterMap.put("outputConfig", outputConfigMap);
    }

    /**
     * 填充校验任务执行规则，包括reader、transformer、writer信息
     *
     * @param job
     * @param parameterMap
     */
    private void fillRuleList(Map<String, Object> parameterMap, JobDTO job) {
        List<Map<String, Object>> rules = new ArrayList<>();

        List<RuleDTO> ruleList = this.ruleService.getRuleListByJobId(job.getId());
        if (CollectionUtils.isNotEmpty(ruleList)) {
            for (RuleDTO rule : ruleList) {
                if (rule != null) {
                    rules.add(fillSingleRule(job, rule));
                }
            }
        }

        parameterMap.put("rules", rules);
    }

    /**
     * 试飞工程值校验，设置文件模板
     *
     * @param parameterMap
     */
    private void setFCTemplateList(Integer fileType, Map<String, Object> parameterMap) {
        // 仅工程值数据校验需要设置文件模板
        if (!fileType.equals(FileType.PROCESS_DATA.getCode())) {
            return;
        }

        // 设置文件采集-文件模板信息
        List<FCTemplateDTO> fileTemplateList = this.qaDataSourceDAO.getFileTemplateList();
        if (CollectionUtils.isEmpty(fileTemplateList)) {
            return;
        }

        parameterMap.put("fileTemplateList", fileTemplateList);
    }

    /**
     * 获取单个规则信息
     *
     * @param job
     * @param rule
     * @return
     */
    private Map<String, Object> fillSingleRule(JobDTO job, RuleDTO rule) {
        Map<String, Object> configMap = new HashMap<>();

        // 填充规则基本信息
        setRuleInfo(rule, configMap);

        // 设置文件信息
        setFileInfo(job, rule, configMap);

        return configMap;
    }

    /**
     * 填充规则基本信息
     *
     * @param rule
     * @param configMap
     */
    private void setRuleInfo(RuleDTO rule, Map<String, Object> configMap) {
        // 设置规则基本信息
        configMap.put("id", rule.getId());
        configMap.put("jobId", rule.getJobId());
        configMap.put("name", rule.getName());
        configMap.put("measureType", rule.getMeasureType());
        configMap.put("measureTypeName", rule.getMeasureTypeName());
        configMap.put("templateType", rule.getTemplateType());
        configMap.put("templateTypeName", rule.getTemplateTypeName());
        configMap.put("templateId", rule.getTemplateId());
        configMap.put("templateName", rule.getTemplateName());
        configMap.put("weight", rule.getWeight());
        configMap.put("weightRatio", rule.getWeightRatio());
        configMap.put("alarmFormula", rule.getAlarmFormula());
        configMap.put("dirtyDataType", rule.getDirtyDataType());
        configMap.put("variableList", rule.getArguementList());

        // 设置算法类型
        RuleTemplateDTO templateDTO = this.templateService.selectByPrimaryKey(rule.getTemplateId());
        if (templateDTO != null) {
            configMap.put("algoType", templateDTO.getAlgoType());
        }
    }

    /**
     * 设置文件信息
     *
     * @param job
     * @param rule
     * @param configMap
     */
    private void setFileInfo(JobDTO job, RuleDTO rule, Map<String, Object> configMap) {
        if (job.getFileType() == null) {
            logger.warn("未获取到文件类型");
            return;
        }

        if (FileType.RAW_DATA.getCode() == job.getFileType()) {
            // 试飞原始文件
            setRawDataInfo(job, rule, configMap);
        } else if (FileType.PROCESS_DATA.getCode() == job.getFileType()) {
            // 试飞工程值数据
            setProcessDataInfo(job, rule, configMap);
        }
    }

    /**
     * 设置试飞原始文件信息
     *
     * @param job
     * @param rule
     * @param configMap
     */
    private void setRawDataInfo(JobDTO job, RuleDTO rule, Map<String, Object> configMap) {
        // 填充文件信息
        String offsiteDataId = job.getOffsiteDataId();
        List<Map<String, Object>> dataSourceList = new ArrayList<>();
        List<FtdpFileDTO> fileList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(rule.getDataSourceList())) {
            for (QaDataSourceDTO ds : rule.getDataSourceList()) {
                if (ds == null) {
                    continue;
                }

                // 获取文件列表
                String whiteList = ds.getWhiteList();
                if (StringUtils.isEmpty(whiteList)) {
                    fileList = this.qaDataSourceDAO.getFileList(offsiteDataId, new ArrayList<>());
                } else {
                    fileList = this.qaDataSourceDAO.getFileList(offsiteDataId, Arrays.asList(whiteList.trim().split(QaConstant.COMMA)));
                }
            }
        } else {
            fileList = this.qaDataSourceDAO.getFileList(offsiteDataId, job.getFileIdList());
        }

        // 获取inf文件（ICD）
        String infFilePath = getInfFilePath(job.getOffsiteDataId());

        // 获取pcm格式文件(剔除.inf格式文件)
        List<FtdpFileDTO> pcmFileList = getPcmFileList(fileList);

        // 获取MinIO配置，包括endPoint、accessKey、secretKey、bucket
        StorageResourceConf conf = FileAdapterUtils.getStorageResourceByProjectId(null);
        String endPoint = conf.getEndPoint();
        String accessKey = conf.getAccessKey();
        String secretKey = conf.getSecretKey();
        String bucket = conf.getStoragePath();
//        String endPoint = "127.0.0.1:9000";
//        String accessKey = "minioadmin";
//        String secretKey = "minioadmin";
//        String bucket = "test";
        if (CollectionUtils.isNotEmpty(pcmFileList)) {
            for (FtdpFileDTO fileDTO : pcmFileList) {
                Map<String, Object> dsMap = new HashMap<>();
                dsMap.put("fileId", fileDTO.getId());
                dsMap.put("inputType", "minio");
                dsMap.put("name", fileDTO.getName());
                dsMap.put("type", fileDTO.getType());
                dsMap.put("size", fileDTO.getSize());
                dsMap.put("fileSuffix", fileDTO.getFileSuffix());
                dsMap.put("pid", fileDTO.getPid());
                dsMap.put("fullName", fileDTO.getFullName());
                dsMap.put("directory", fileDTO.getDirectory());
                dsMap.put("status", fileDTO.getStatus());
                dsMap.put("creationDate", fileDTO.getCreationDate());
                dsMap.put("enable", fileDTO.getEnable());
                dsMap.put("endPoint", endPoint);
                dsMap.put("accessKey", accessKey);
                dsMap.put("secretKey", secretKey);
                dsMap.put("bucketName", bucket);
                dsMap.put("icdFilePath", infFilePath);
                dsMap.put("inputFilePath", fileDTO.getFullName());
                dataSourceList.add(dsMap);
            }
        }

        configMap.put("dataSourceList", dataSourceList);
    }

    /**
     * 设置试飞工程值文件信息
     *
     * @param job
     * @param rule
     * @param configMap
     */
    private void setProcessDataInfo(JobDTO job, RuleDTO rule, Map<String, Object> configMap) {
        if (StringUtils.isEmpty(job.getResourceIds())) {
            return;
        }

        // 获取文件信息
        List<FtdpFileDTO> fileList = this.qaDataSourceDAO.getFileList(null, Arrays.asList(job.getResourceIds().trim().split(QaConstant.COMMA)));
        if (CollectionUtils.isEmpty(fileList)) {
            return;
        }

        // 获取MinIO配置，包括endPoint、accessKey、secretKey、bucket
        StorageResourceConf conf = FileAdapterUtils.getStorageResourceByProjectId(null);
        String endPoint = conf.getEndPoint();
        String accessKey = conf.getAccessKey();
        String secretKey = conf.getSecretKey();
        String bucket = conf.getStoragePath();
//        String endPoint = "127.0.0.1:9000";
//        String accessKey = "minioadmin";
//        String secretKey = "minioadmin";
//        String bucket = "test";

        // 填充工程值数据信息
        List<Map<String, Object>> dataSourceList = new ArrayList<>();
        for (FtdpFileDTO fileDTO : fileList) {
            Map<String, Object> dsMap = new HashMap<>();
            dsMap.put("fileId", fileDTO.getId());
            dsMap.put("dbType", "minio");
            dsMap.put("name", fileDTO.getName());
            dsMap.put("type", fileDTO.getType());
            dsMap.put("size", fileDTO.getSize());
            dsMap.put("fileSuffix", fileDTO.getFileSuffix());
            dsMap.put("pid", fileDTO.getPid());
            dsMap.put("fullName", fileDTO.getFullName());
            dsMap.put("directory", fileDTO.getDirectory());
            dsMap.put("status", fileDTO.getStatus());
            dsMap.put("creationDate", fileDTO.getCreationDate());
            dsMap.put("enable", fileDTO.getEnable());
            dsMap.put("endPoint", endPoint);
            dsMap.put("accessKey", accessKey);
            dsMap.put("secretKey", secretKey);
            dsMap.put("bucket", bucket);
            dataSourceList.add(dsMap);
        }

        configMap.put("dataSourceList", dataSourceList);
    }

    /**
     * 获取inf文件全路径名称
     *
     * @param ftdpOffsiteDataId
     * @return
     */
    private String getInfFilePath(String ftdpOffsiteDataId) {
        if (StringUtils.isEmpty(ftdpOffsiteDataId)) {
            logger.error("试飞信息ID为空.");
            return null;
        }

        String infFileFullName = this.ftdpResourceApi.getInfPathById(ftdpOffsiteDataId);
        if (StringUtils.isEmpty(infFileFullName)) {
            logger.warn("获取inf文件失败,ftdpOffsiteDataId:{}.", ftdpOffsiteDataId);
            return null;
        }

        return infFileFullName;
    }

    /**
     * 获取pcm文件列表
     * 注意；附件中可能包含PCM原始文件和INF文件，所以要过滤掉.inf文件
     *
     * @param fileList
     * @return
     */
    private List<FtdpFileDTO> getPcmFileList(List<FtdpFileDTO> fileList) {
        if (CollectionUtils.isEmpty(fileList)) {
            return fileList;
        }

        return fileList.stream().filter(item -> !(item.getName().endsWith(INF_FILE_SUFFIX))).collect(Collectors.toList());
    }
}
